package net.csiit.platform.codeguard.classloader;

import net.csiit.platform.codeguard.util.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

/**
 * @author adam
 * @date 2020/7/3
 */
public class CodeGuardConfiguration {

    public static final String CONFIG_FILE_NAME = "code-guard.properties";
    public static final String CMD_LINE_CONFIG_PARAM = "code.guard.conf";

    private static String key;
    private static Set<String> packages;
    private static String bootFatJar;
    private static Set<String> basePackages;

    public synchronized static void loadConfig(String currentJarDir) throws IOException {
        if (!StringUtils.isEmpty(key)) {
            //already loaded
            return;
        }

        String path = lookupEnv(CMD_LINE_CONFIG_PARAM);
        if (!StringUtils.isEmpty(path)) {
            File file = new File(path);
            parseConfig(file);
            return;
        }

        String userdir = lookupEnv("user.dir");
        String userhome = lookupEnv("user.home").concat(File.separator).concat(".guard");
        String[] array = new String[]{userdir, userhome, currentJarDir};

        File file = null;
        for (String dir : array) {
            if (dir == null) {
                continue;
            }

            File f = new File(dir.concat(File.separator).concat(CONFIG_FILE_NAME));
            if (f.exists() && f.isFile()) {
                file = f;
                break;
            }
        }

        if (file == null) {
            throw new RuntimeException("Can not found configure file !");
        }

        parseConfig(file);
    }

    public static boolean matches(String className) {
        if (className.contains("/")) {
            className = className.replace("/", ".");
        }

        for (String decryptPackage : packages) {
            if (className.startsWith(decryptPackage) || className.matches(decryptPackage)) {
                return true;
            }
        }

        return false;
    }

    public static boolean isInBasePackage(String name) {
        for (String basePackage : basePackages) {
            if (name.startsWith(basePackage) || name.matches(basePackage)) {
                return true;
            }
        }

        return false;
    }

    private static void parseConfig(File file) throws IOException {
        Properties properties = new Properties();
        try (InputStream inputStream = new FileInputStream(file)) {
            properties.load(inputStream);
        }

        key = properties.getProperty("key");
        String packagesString = properties.getProperty("packages");
        if (!StringUtils.isEmpty(packagesString)) {
            String[] packagesArray = packagesString.split(",");
            packages = new HashSet<>(Arrays.asList(packagesArray));
        }

        bootFatJar = properties.getProperty("bootFatJar");
        String basePackageStr = properties.getProperty("basePackages");
        if (!StringUtils.isEmpty(basePackageStr)) {
            String[] basePackagesArray = basePackageStr.split(",");
            basePackages = new HashSet<>(Arrays.asList(basePackagesArray));
        }
    }

    private static String lookupEnv(String key) {
        String val = System.getProperty(key);
        if (!StringUtils.isEmpty(val)) {
            return val;
        }

        return System.getenv(key);
    }

    public static String getKey() {
        return key;
    }

    public static Set<String> getPackages() {
        return packages;
    }

    public static String getBootFatJar() {
        if (bootFatJar != null) {
            return bootFatJar;
        }

        return inspectBootFatJar();
    }

    public static String inspectBootFatJar() {
        String userdir = lookupEnv("user.dir");
        if (!StringUtils.endsWith(userdir, File.separator)) {
            userdir = userdir + File.separator;
        }
        String[] possiblePaths = new String[]{lookupEnv("java.class.path"), lookupEnv("sun.java.command")};

        File jarFile = null;
        for (String path : possiblePaths) {
            if (!StringUtils.endsWith(path, ".jar")) {
                continue;
            }

            jarFile = new File(path);
            if (jarFile.exists()) {
                return jarFile.getAbsolutePath();
            }

            jarFile = new File(userdir + path);
            if (jarFile.exists()) {
                return jarFile.getAbsolutePath();
            }
        }

        return null;
    }
}
